/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "rdx_pragmas.hpp"

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <new>
#include <emmintrin.h>

#undef CreateMutex

#include "rdx_threading.hpp"
#include "rdx_longflow.hpp"
#include "rdx_objectmanagement.hpp"

namespace RDX
{
	namespace Threading
	{
		using namespace RDX::ObjectManagement;

		struct Win32Mutex;

		struct Win32MutexProcessor : public ITypeProcessor
		{
			void Finalize(void *obj, IObjectManager *objm) const;
			void MarkDependencies(IObjectManager *objm, void *obj, const Programmability::StructuredType *st, bool markNative, ISerializer *ser, ScanID scanID, RDX::ObjectManagement::GCLink gcl) const;
			bool OnLoad(OperationContext *ctx, void *obj, IObjectManager *objm) const;

			static Win32MutexProcessor instance;
		};
		Win32MutexProcessor Win32MutexProcessor::instance;

		struct Win32Mutex : public IMutex
		{
			CRITICAL_SECTION cs;
			bool initialized;

			virtual bool TryAcquire(int msec)
			{
				return (TryEnterCriticalSection(&cs) != 0);
			}

			virtual void Acquire(int msec)
			{
				EnterCriticalSection(&cs);
			}

			virtual void Release()
			{
				LeaveCriticalSection(&cs);
			}

			virtual void Destroy()
			{
				if(initialized)
					DeleteCriticalSection(&cs);
			}
		};

		DWORD WINAPI Win32StartThread(LPVOID lpParameter)
		{
			const ISystemThreadProc *proc = static_cast<const ISystemThreadProc *>(lpParameter);
			proc->Start();
			return 0;
		}

		class Win32Thread : public ISystemThread
		{
		private:
			HANDLE _handle;
		public:
			Win32Thread(const ISystemThreadProc *proc)
			{
				_handle = CreateThread(NULL, 0, Win32StartThread, const_cast<ISystemThreadProc*>(proc), 0, NULL);
			}

			~Win32Thread()
			{
				CloseHandle(_handle);
			}

			bool WaitFor(int msec)
			{
				return (WaitForSingleObject(_handle, static_cast<DWORD>(msec < 0 ? INFINITE : msec)) == WAIT_OBJECT_0);
			}

			bool IsActive()
			{
				DWORD exitCode;
				GetExitCodeThread(_handle, &exitCode);
				return exitCode == STILL_ACTIVE;
			}
		};

		ISystemThread *CreateSystemThread(const ISystemThreadProc *proc)
		{
			return new Win32Thread(proc);
		}

		void DestroySystemThread(ISystemThread *sysThread)
		{
			delete sysThread;
		}

		void SleepThread(LargeInt msec)
		{
			Sleep(static_cast<DWORD>(msec));
		}

		ISystemThread::~ISystemThread()
		{
		}

		int CountProcessors()
		{
			SYSTEM_INFO sysinfo;
			GetSystemInfo(&sysinfo);
			return static_cast<int>(sysinfo.dwNumberOfProcessors);
		}

		CRef<IMutex> CreateMutex(OperationContext *ctx, IObjectManager *objm)
		{
			RDX_TRY(ctx)
			{
				CRef<Win32Mutex> mtx;
				RDX_PROTECT_ASSIGN(ctx, mtx, objm->CreateContainer(ctx, sizeof(Win32Mutex), 1, 0, DOMAIN_Runtime, NULL, &Win32MutexProcessor::instance, 0).Cast<Win32Mutex>() );
				new (static_cast<Win32Mutex*>(mtx)) Win32Mutex();

				// This never fails under Vista
				if(InitializeCriticalSectionAndSpinCount(&mtx->cs, 4000) == 0)
					RDX_STHROW(ctx, Errors::RDX_ERROR_ALLOCATION_FAILED);
				mtx->initialized = true;

				return mtx.Cast<IMutex>();
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROWV(ctx, NULL);
			}
			RDX_ENDTRY
		}

		void Win32MutexProcessor::Finalize(void *obj, IObjectManager *objm) const
		{
			Win32Mutex *mtx = reinterpret_cast<Win32Mutex*>(obj);
			mtx->Destroy();
		}

		void Win32MutexProcessor::MarkDependencies(IObjectManager *objm, void *obj, const Programmability::StructuredType *st, bool markNative, ISerializer *ser, ScanID scanID, RDX::ObjectManagement::GCLink gcl) const
		{
		}

		bool Win32MutexProcessor::OnLoad(OperationContext *ctx, void *obj, IObjectManager *objm) const
		{
			return true;
		}


		NonAtomicInt AtomicIncrement(volatile AtomicInt *v)
		{
			return InterlockedIncrement(&v->value);
		}

		NonAtomicInt AtomicDecrement(volatile AtomicInt *v)
		{
			return InterlockedDecrement(&v->value);
		}

		NonAtomicInt NonAtomicDecrement(volatile AtomicInt *v)
		{
			return v->value--;
		}

		NonAtomicInt AtomicRead(const volatile AtomicInt *v)
		{
			return v->value;
		}

		NonAtomicInt NonAtomicRead(const volatile AtomicInt *v)
		{
			return v->value;
		}

		void AtomicWrite(volatile AtomicInt *v, NonAtomicInt value)
		{
			Synchronize();
			v->value = value;
		}

		void NonAtomicWrite(volatile AtomicInt *v, NonAtomicInt value)
		{
			v->value = value;
		}


		void Synchronize()
		{
			_mm_sfence();
		}

		NonAtomicInt AtomicCompareExchange(volatile AtomicInt *exchangeAddr, NonAtomicInt replacement, NonAtomicInt compareTo)
		{
			return InterlockedCompareExchange(&exchangeAddr->value, replacement, compareTo);
		}
	}
}
